RISC-V处理器 (一.RISC-V指令集)

  1. 1. 一.RISC-V
  2. 2. 二.RISC-V指令
    1. 2.1. RISC-V指令结构
      1. 2.1.1. RV32I 基础整数指令集
        1. 2.1.1.1. 寄存器的数学/逻辑运算指令
        2. 2.1.1.2. 立即数的数学/逻辑运算指令
        3. 2.1.1.3. 内存读取指令
        4. 2.1.1.4. 内存储存指令
        5. 2.1.1.5. 程序计数器的分支跳转
        6. 2.1.1.6. 其他指令

本文为通过Verliog HDL编写简易RISC-V CPU的试验笔记,如有错误敬请指正


一.RISC-V

RISC-V为伯克利研究团队基于精简指令集计算原理建立的一个开放指令集架构。由于其开源、模型化及指令长度固定等特点而被应用于学习及嵌入式领域当中。

详见:什么是RISC-V-知乎

二.RISC-V指令

作为大型开源项目,RISC-V的官方技术文档可于Github上搜索得到并下载。此外,CSDN及知乎上亦有相关指令集介绍与表格可供参考,因此本文仅对RISC-V的指令架构作简略描述。

RISC-V指令结构

RISC-V指令包含数个主要结构,分别为访问寄存器的R-Type,使指指令中立即数进行操作的I-Type,储存至内存中的S-Type,判断分支执行的B-Type,执行跳转的J-Type,以及包含20位立即数的U-Type。

对于下方图表,opcode为指令操作码,rs1与rs2均为源寄存器地址,rd为目标寄存器地址,funct3及funct7为操作指令,imm为立即数,即计算机对该数据进行简易操作(扩展)后直接传递至计算单元进行运算的数据,其特点为数据被保存至指令中而无需访问寄存器。下表中imm[ X1:X2X_1:X_2 ]表示数据的长度为X1X2+1X_1-X2+1位二进制编码,其顺序按高至低位排列,如11-0,20-0。

主要指令结构

RV32指令集的指令位宽及数据位寛均为32位,寄存器地址长度为5位,最多可支持25=322^5=32个寄存器,此外,由于指令不支持直接写入结果至内存,该指令集对寄存器的操作频率将较其他指令集高。

RV32I 基础整数指令集

RV32I Base Integer Instructions ( 32位基础整数指令集 )

RV32I主要包含37条运行指令与2条系统指令。相应的指令表格可通过RSIC-V Reference Card或RISC官方技朮文档得到。

RISC-V Reference Card

1
2
3
4
5
指令按操作类型大致分为
-寄存器的数学/逻辑运算指令
-立即数的数学/逻辑运算指令
-内存的数据储存/访问指令
-程序计数器的分支跳转/无条件跳转指令

详见:

RISC-V Reference Card - Github

RISC-V Instruction Set Manual - RISC-V International

寄存器的数学/逻辑运算指令

按R-Type结构进行编码,其指令操作码均为七位二进位数的(0110011),按funct3判断数据的运算类型。

以算朮加法ADD为例,其funct3为三位二进制的000,操作内容为从地址rs1及rs2对应的32位寄存器中取数据后通过算朮单元进行算朮加法,随后输出计算结果并储存至rd地址所对应的32位寄存器中。完成此流程则认为该指令执行完结。

下式为ADD指令的一个例子,该指令的rd为01111201111_2,rs1为01110201110_2,rs2为01111201111_2,funct3为0002000_2,funct7为000000020000000_2

00F707B316:0000000(funct7)01111(rs2)01110(rs1)000(funct3)01111(rd)0110011(opcode)200F707B3_{16} : 0000000_{(funct7)}01111_{(rs2)}01110_{(rs1)}000_{(funct3)}01111 _{(rd)}0110011_{(opcode)2}

附注:

SUB(算朮减法)指令与ADD共用一个funct3值,需通过funct7进行判别。

SRA(循环右移)指令与SRL共用一个funct3值,需通过funct7进行判别。

SRA(循环右移)指令将数据的最右位循环至数据的的最高位。

SLT指令视数据为符号数进行比较,SLTU指令则视数据为无符号数进行比较。

立即数的数学/逻辑运算指令

按I-Type结构进行编码,其指令操作码均为七位二进位数的(0010011),按funct3判断数据的运算类型。

以算朮加法ADDI为例,其funct3为三位二进制的000,执行步骤为从地址rs1对应的32位寄存器中取数据,同时扩展立即数至32位数值,随后通过算朮单元进行算朮加法,完成后输出计算结果储存至地址rd所对应的32位寄存器中。完成此流程则认为该指令执行完结。

下式为ADDI指令的一个例子,其所对应的rd为01111201111_2,rs1为01111201111_2,funct3为0002000_2

0017879316:000000000001(imm[11:0])01111rs1000funct301111rd0010011(opcode)200178793_{16} : 000000000001_{(imm[11:0])} 01111_{rs1} 000_{funct3} 01111_{rd}001 0011_{(opcode)2}

附注:

一. 符号扩展:

立即数指令默认进行符号扩展。视所有立即数通过补码形式储存。若立即数最高位为1,则扩展时对其高20位补1。否则对高20位补0。

32位立即数扩展后的结构为`imm[31:0] = { imm[31:12], imm[11:0] }`

1
2
if( imm[11] == 1 ) 	imm[31:12] = FFFFF;	//高20位为1
else imm[31:12] = 00000; //高20位为0

二. 零扩展:

无论立即数的高位为何,其高位皆补零。

内存读取指令

按I-Type结构进行编码,其指令操作码均为七位二进位数的(0000011),按funct3判断数据的储存方式。

以字加载LW为例,其funct3为三位二进制的010,该指令先从地址rs1中取数据,与符号扩展的12位立即数进行符号算朮加法,并输出计算结果至32位地址总线,内存则通过地址总线上的值读出数据。并写入该数据至地址rd对应的寄存器。

1
2
3
4
5
6
LW字加载: 读入四个字节M[31:0]至rd。
LH半字加载:读入二个字节M[15:0]至rd。对高16位采用符号扩展。
LB字节加载:读入一个字节M[ 7:0]至rd。对高24位采用符号扩展。

LHU无符号半字加载:读入二个字节M[15:0]至rd。对高16位采用零扩展。
LBU无符号字节加载:读入二个字节M[ 7:0 ]至rd。对高24位采用零扩展。

下式为LW指令的一个例子,其以rs1寄存器的值为基点偏移(2410-24_{10})后的内存位置读出一个字至rd寄存器中。

FE84270316:111111101000(imm[11:0])01000(rs1)010(funct3)01110(rd)0000011(opcode)2FE842703_{16} : 111111101000_{(imm[11:0])} 01000_{(rs1)} 010_{(funct3)} 01110_{(rd)} 0000011_{(opcode)2}

附注:

假设rs1中的值为32,则访问的内存位置为(32+(-24)) = 8,即rd = M[8][31:0]

上述表格未表示M的内存地址,完整的描述为M[rs1+imm][31:0]

内存储存指令

按S-Type结构进行编码,其指令操作码均为七位二进位数的(0100011),按funct3判断数据的储存方式。

以字加载SW为例,其funct3为三位二进制的010,该指令先从rs1中取数据,与符号扩展的12位立即数进行符号算朮加法,并输出计算结果至32位地址总线,指令同时取出rs2的值输出至数据总线。内存则通过地址总线及数据总线上的值。写入该数据至地址对应的区域。

1
2
3
SW储存字: 写入四个字节rs2[31:0]至M[rs1+imm][31:0]。
LH储存半字:写入二个字节rs2[15:0]至M[rs1+imm][15:0]。
LB储存字节:写入一个字节rs2[ 7:0 ]至M[rs1+imm][7:0]。

下式为SW指令的一个例子,该指令将rs2的值写入以rs1为基点偏移(2010-20_{10})后的内存位置。

FEF4262316:1111111(imm[11:5])01111(rs2)01000(rs1)010(funct3)01100(imm[4:0])0100011(opcode)2FEF4 2623_{16} : 1111111_{(imm[11:5])} 01111_{(rs2)} 01000_{(rs1)} 010_{(funct3)} 01100_{(imm[4:0])} 0100011_{(opcode)2}

程序计数器的分支跳转

按B-Type结构进行编码,其指令操作码均为七位二进位数的(1100011),按funct3实施不同的跳转策略。

以大于跳转BGE为例,其funct3为三位二进制的101,该指令先从rs1与rs2中取数据,并判断rs1是否大等于于rs2,若真则修改PC的值为PC=PC+imm(符号扩展)PC = PC+imm_{(符号扩展)}。若否则使PC=PC+4PC = PC + 4

下式为BGE指令的一个例子,若rs1值大于等于rs2,则PC跳转至PC2416PC - 24_{16}

FCE7DEE316:1111110imm[1210:5]01110(rs2)01111(rs1)101(funct3)11101(imm[4:111])1100011(opcode)2FCE7 DEE3_{16} : 1111110_{imm[12|10:5]} 01110_{(rs2)} 01111_{(rs1)} 101_{(funct3)} 11101_{(imm[4:1|11])} 1100011_{(opcode)2}

附注:

程序计数器( Program Counter ) - 百度百科

BLTU及BGEU对立即数进行零扩展而非符号扩展。

其他指令

由于所有RV32I指令均已在上表中给出说明,故其他指令不作描述。

RISC-V-Logo